#!/usr/bin/perl
package Backup::Read;
use strict;
use File::Basename;

# This class is intended to help with reading backups, it was build alongside restore.pl to provide a user friendly interface to restoring mysql databases
# This class can also be used in any future restoration script, and can assist in the automation of restoration if need be.

# backups are specified as a folder with a backup.ini file properly formatted containing the contents of the backup (by database and tables), and each database compressed into a .tar.gz file.
# This program checks for inconsistencies and will return error codes (but will not quit the program unless the folder specified is not a backup)

sub new {
	# Class Backup ( backupFolder )
		# backupFolder = The folder location of the backup.  Ex /home/somedev3/bin/backups/20110705
	my ($type,$backupFolder) = @_;
	$backupFolder = dirFormat($backupFolder);
	# Define class as backup
	my $self = {backupFolder=>$backupFolder,
		backupName=>basename($backupFolder),
		backupINI=>"$backupFolder/backup.ini",
		keyHash=>{},
		query=>"",
	};
	# Make sure its actually a backup and not just a random folder
	bless ($self,$type);
	if ( -f "$backupFolder/backup.ini" ) {
		$self->readINI();
	} else {
		print STDERR "The specified folder $backupFolder is not in correct backup format.\n";
		exit;
	}
	return $self;
}

sub addDatabase {
	# Subroutine addDatabase ( fromDB, [toDB] )
		# fromDB = the database you wish to restore
		# toDB = the database you wish to restore to... this database MUST exist (this program will not create databases for you)
	# This subroutine adds a database restore to the query
	# This subroutine will not kill the class if there is an error, instead it will return error codes.
	# Error Codes
		# 0 = Subroutine executed successfully
		# 1 = Specified input database does not exist in the backup
	my ($self,$fromDB,$toDB) = @_;
	# Check if the database is in the backup
	if ( !($self->inBackup($fromDB)) ) {
		#database doesnt exist
		print STDERR "$fromDB does not exist in the backup. Skipping...\n";
		return 1;
	}
	# Extract the database or use existing files
	$self->extract($fromDB);
	opendir(DIR,"$self->{backupFolder}/$fromDB");
	my @sql = readdir(DIR);
	closedir(DIR);
	# Loop through all the tables, adding each
	foreach (@sql) {
		if ( -f "$self->{backupFolder}/$fromDB/$_" ) {
			$_ =~ /(.*)\.sql/;
			$self->addTable($fromDB,$toDB,$1);
		}
	}
	return 0;
}

sub addTable {
	# Subroutine addTable ( fromDB, [toDB], fromTable, [toTable] )
		# fromDB = the database you wish to restore
		# toDB = the database you wish to restore to... this database MUST exist (this program will not create databases for you)
		# fromTable = the table you wish to restore
		# toTable = the table you wish to restore to
		# If you don't want to define toDB or toTable just leave them as undef
	# This subroutine will add a single table to a database, and is utilized by the addDatabase subroutine above
	# It allows for copying tables instead of restoring in addition to cross database restoration
	my ($self,$fromDB,$toDB,$fromTable,$toTable) = @_;
	if ( !$fromDB ) {
		print STDERR "You must define the database the table is in.\n";
	}
	if ( !$fromTable ) {
		print STDERR "You did not specify a table.\n";
	}
	if ( !($self->inBackup($fromDB,$fromTable)) ) {
		print STDERR "Table $fromTable in database $fromDB does not exist in the backup. Skipping...\n";
		return 1;
	}
	$self->extract($fromDB);
 	open(FILE,"$self->{backupFolder}/$fromDB/$fromTable.sql");
	$/ = undef;
	my $contents = <FILE>;
	if ( $toTable ) {
		$contents =~ s/$fromTable/$toTable/g;
	}
	if ( $toDB ) {
		$self->addToQuery("USE $toDB;");
	} else {
		$self->addToQuery("USE $fromDB;");
	}
	$self->addToQuery($contents);
	return 0;
}

sub inBackup {
	# Subroutine inBackup ( db, [table] )
		# db = the database you are checking to see
		# table = the table you are checking to see
	# This subroutine will check if a database or table is packaged into a backup, returns 1 for yes, 0 for no.
	my ($self,$db,$table) = @_;
	my @databasesInBackup = keys %{$self->{keyHash}};
	my $is_in = {};
	for (@databasesInBackup) { $is_in->{$_} = 1 }
	if ( $is_in->{$db} ) {
		if ( $table ) {
			my @tablesInDatabase = @{$self->{keyHash}->{$db}};
			$is_in = {};
			for (@tablesInDatabase) { $is_in->{$_} = 1 }
			if ( $is_in->{$table} ) {
				return 1;
			} else {
				return 0;
			}
		} else {
			return 1;
		}
	} else {
		return 0;
	}
}

sub addToQuery {
	# Subroutine addToQuery ( queryText )
		# queryText = the query line you want to add to the cumulative query
	# This is a helper method to build cumulative queries
	my ($self,$queryText) = @_;
	$self->{query} = $self->{query}.$queryText."\n";
}

sub clear {
	# Clears the query
	my ($self) = @_;
	$self->{query} = "";
}

sub cleanup {
	# Removes extracted folders
	my ($self) = @_;
	opendir(DIR,$self->{backupFolder});
	my @file = readdir(DIR);
	foreach (@file) {
		if ( -d "$self->{backupFolder}/$_" && !($_ =~ /\.$/)) {
			system("rm -rf $self->{backupFolder}/$_");
		}
	}	
}

sub extract {
	# Subroutine extract ( database )
		# database = the name of the database.tar.gz file you want to extract (dont include .tar.gz)
	# Extract .tar.gz files if not already extracted
	my ($self,$database) = @_;
	if ( -d "$self->{backupFolder}/$database" ) {
		# Check if the database is already extracted (This is simply for efficiency, and allows additional functionality of editing databases pre restore)
		return;
	} else { 
		system("cd $self->{backupFolder};tar -xzf $database.tar.gz;");
		return;
	}
}

sub readINI {
        # This subroutine maps data from backup.ini files into a readable hash
        my($self) = @_;
        open(FILE,$self->{backupINI});
        my $hash = {};
        my $currentHeader = "";
        foreach my $line (<FILE>) {
                if ( $line =~ /\t(.*)\n/ ) {
                        push @{$hash->{$currentHeader}},$1;
                } else {
                        $line =~ /(.*)\n/;
                        $hash->{$1} = ();
                        $currentHeader = $1;
                }
        }
        close(FILE);
	$self->{keyHash} = $hash;
}

sub getINI {
	my ($self) = @_;
	return $self->{keyHash};
}

sub getQuery {
	my ($self) = @_;
	return $self->{query};
}

sub dirFormat {
	my ($string) = @_;
	$string =~ s/\/$//;
	return $string;
}

1;
